package com.mspbots.ticketai.graderule;

import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.mspbots.ticketai.GradeRuleInterceptorAdapter;
import com.mspbots.ticketai.model.Ticket;
import com.mspbots.ticketai.model.TicketRule;
import com.mspbots.ticketai.model.TicketSchedule;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;

import java.time.Duration;
import java.time.LocalDateTime;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;

/**
 * @author Jay.Yang
 */
@Slf4j
@Service(value = "SCHEDULED_STATUS_POINT_RULE")
public class ScheduledStatusRuleInterceptor extends GradeRuleInterceptorAdapter {

    private List<ScheduledStatusParam> params;

    @Override
    public void prepare(TicketRule rule) {
        params = this.objectMapper.convertValue(rule.getParams(), new TypeReference<>() {
        });
    }

    @Override
    public void process(Ticket ticket) {
        AtomicInteger order = new AtomicInteger();
        ObjectNode orderDetailNode = ticket.getOrderDetail() == null ? this.objectMapper.createObjectNode() : ticket.getOrderDetail().deepCopy();
        ObjectNode itemsNode = this.objectMapper.createObjectNode();
        TicketSchedule ticketSchedule = null;
        if (!CollectionUtils.isEmpty(ticket.getTicketScheduleList())) {
            boolean isAssignedSchedule = false;
            if (!StringUtils.isEmpty(ticket.getIdentifier())) {
                isAssignedSchedule = true;
                ticketSchedule = ticket.getTicketScheduleList().stream()
                        .filter(s -> s.getDateStart() != null)
                        .filter(s -> !StringUtils.isEmpty(s.getIdentifier())
                                && Arrays.stream(StringUtils.commaDelimitedListToStringArray(s.getIdentifier()))
                                .collect(Collectors.toList()).contains(ticket.getIdentifier()))
                        .min(Comparator.comparing(TicketSchedule::getDateStart)).orElse(null);
            }
            if (ObjectUtils.isEmpty(ticketSchedule)) {
                isAssignedSchedule = false;
                ticketSchedule = ticket.getTicketScheduleList().stream()
                        .filter(s -> s.getDateStart() != null)
                        .min(Comparator.comparing(TicketSchedule::getDateStart)).orElse(null);
            }
            if (ticketSchedule != null) {
                boolean finalIsAssignedSchedule = isAssignedSchedule;
                long time = Duration.between(LocalDateTime.now(), ticketSchedule.getDateStart()).toMinutes();
                params.stream().filter(item -> item.isSelected() && item.getStatusIds().contains(ticket.getStatusId()))
                        .forEach(item -> {
                            if ("moreThan".equalsIgnoreCase(item.getCompareType()) && time >= item.getMinutes()) {
                                if (StringUtils.isEmpty(ticket.getIdentifier()) || item.getPoints() <= 0) {
                                    order.addAndGet(item.getPoints());
                                } else if (finalIsAssignedSchedule && item.getPoints() > 0) {
                                    order.addAndGet(item.getPoints());
                                }
                            } else if ("lessThan".equalsIgnoreCase(item.getCompareType()) && time <= item.getMinutes()) {
                                if (StringUtils.isEmpty(ticket.getIdentifier()) || item.getPoints() <= 0) {
                                    order.addAndGet(item.getPoints());
                                } else if (finalIsAssignedSchedule && item.getPoints() > 0) {
                                    order.addAndGet(item.getPoints());
                                }
                            }
                        });
            }

        }
        itemsNode.put("scheduleStartTime", ticketSchedule != null ? ticketSchedule.getDateStart().toString() : "");
        itemsNode.put("scheduleIdentifier", ticketSchedule != null ? ticketSchedule.getIdentifier() : "");
        itemsNode.put("point", order.get());
        orderDetailNode.set("scheduleStatus", itemsNode);
        ticket.setOrderDetail(orderDetailNode);
        ticket.setOrder(ticket.getOrder() + order.get());
    }

    @Data
    private static class ScheduledStatusParam {
        private Integer index;
        private Integer points;
        private Integer minutes;
        private List<Integer> statusIds;
        private boolean selected;
        private String compareType;
    }
}
